//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#include "ComplexesDB.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>

#include <iostream>
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#define LENGTHLINE 5000
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
using namespace std;
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ComplexesDB::ComplexesDB() {
  this->listIDPDB  = new TListE <String> ();
  this->locationDB = new String();
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ComplexesDB::ComplexesDB(const ComplexesDB& complexesDB) {
  this->listIDPDB  = new TListE <String> (complexesDB.listIDPDB);
  this->locationDB = new String(complexesDB.locationDB);
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ComplexesDB::ComplexesDB(const ComplexesDB* complexesDB) {
  this->listIDPDB  = new TListE <String> (complexesDB->listIDPDB);
  this->locationDB = new String(complexesDB->locationDB);
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
ComplexesDB::~ComplexesDB() {
  if (this->listIDPDB)  delete this->listIDPDB;
  if (this->locationDB) delete this->locationDB;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ComplexesDB::ListIDPDB(TListE<String>* listIDPDB) {
    
  if (listIDPDB) {
    if (this->listIDPDB) delete this->listIDPDB;
    this->listIDPDB = listIDPDB;
  }

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ComplexesDB::LocationDB(String* locationDB) {

  if (locationDB) {
    if (this->locationDB) delete this->locationDB;
    this->locationDB = locationDB;
  }
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
TListE <String>* ComplexesDB::ListIDPDB(void) {
  return this->listIDPDB;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* ComplexesDB::LocationDB(void) {
  return this->locationDB;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ComplexesDB::GetListComplexes() {


    TListE <String> *listFiles = new TListE <String> ();

    /*Load the path for all pdb file inside the selected location*/
    struct dirent **namelist;
    int             n;

    n = scandir(this->locationDB->Out(), &namelist, 0, alphasort);

    if (n >= 0) {

      while(n--) {

        if (strcmp(namelist[n]->d_name, ".") && strcmp(namelist[n]->d_name, "..")) {
          String* nameFile = new String(namelist[n]->d_name);
          listFiles->Add(nameFile);
        }

        free(namelist[n]);

      }

      free(namelist);
      
    }

    char buffer[LENGTHLINE + 1];
    char completePath[strlen(this->locationDB->Out()) + LENGTHLINE];

    cout << "Reading Files..." << endl;
    cout << "---------------------------------------------------------------------------------" << endl;
    
    while (listFiles->Get(0)) {

      strcpy(completePath, this->locationDB->Out());
      strcat(completePath, listFiles->GetCurrent()->Out());
      cout << "File: " << completePath << endl;

      gzFile pdbFile     = gzopen(completePath, "rb9");
      bool   condAnalyze = false;
      bool   condHETATM  = false;

      while (!gzeof(pdbFile)) {

        gzgets(pdbFile, buffer, LENGTHLINE);

        if (condAnalyze) {
            
          if (!strncmp(buffer, "ATOM", strlen("ATOM"))) {
            condAnalyze = false;
            continue;
          }

          if (!strncmp(buffer, "HETATM", strlen("HETATM"))) {

            String* tmpBuffer = new String(buffer);
            String* atomType  = tmpBuffer->SubStr(12, 16);
            String* resName   = tmpBuffer->SubStr(17, 20);

            atomType->RTrim();
            resName->RTrim();

            if (tmpBuffer) delete tmpBuffer;

            if (strcmp(atomType->Out(), resName->Out()) && strcmp("HOH", resName->Out())) {
              condHETATM = true;
              this->listIDPDB->Add(listFiles->TransferCurrent());
              if (atomType) delete atomType;
              if (resName)  delete resName;
              break;
            }

            if (atomType) delete atomType;
            if (resName)  delete resName;

          }
        }

        if (!strncmp(buffer, "TER", strlen("TER")))
          condAnalyze = true;
            
      }

      if (!condAnalyze || !condHETATM) listFiles->DeleteCurrent();

      gzclose(pdbFile);

    }

    if (listFiles) delete listFiles;

    return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ComplexesDB::GetListComplexesWithRestrictions() {


  TListE <String>* listFiles = new TListE <String> ();
  TListE <String>* lines     = new TListE <String> ();
  TListE <int>*    listResN  = new TListE <int>    ();


  /*Load the path for all pdb file inside the selected location*/
  struct dirent **namelist;
  int             n;

  n = scandir(this->locationDB->Out(), &namelist, 0, alphasort);

  if (n >= 0) {
    while(n--) {
      if (strcmp(namelist[n]->d_name, ".") && strcmp(namelist[n]->d_name, "..")) {
        String* nameFile = new String(namelist[n]->d_name);
        listFiles->Add(nameFile);
      }
      free(namelist[n]);
    }
    free(namelist);
  }

  char    buffer[LENGTHLINE + 1];
  char    completePath[strlen(this->locationDB->Out()) + LENGTHLINE];

  cout << "Reading Files..." << endl;
  cout << "---------------------------------------------------------------------------------" << endl;

  while (listFiles->Get(0)) {

    sprintf(completePath, "%s%s\0", this->locationDB->Out(), listFiles->GetCurrent()->Out());
    cout << "File: " << completePath << endl;

    String* tmpChain = new String();
    String* tmpResN  = new String();

    bool    resNChange = false;

    gzFile  pdbFile  = gzopen(completePath, "rb9");

    lines->Clear();
    listResN->Clear();
    
    while (!gzeof(pdbFile)) {

      gzgets(pdbFile, buffer, LENGTHLINE);

      if (!strncmp(buffer, "HETATM", strlen("HETATM"))) {

        String* tmpBuffer = new String(buffer);
        String* atomType  = tmpBuffer->SubStr(12, 16);
        String* resName   = tmpBuffer->SubStr(17, 20);
        String* chain     = tmpBuffer->SubStr(21, 22);
        String* resNumber = tmpBuffer->SubStr(22, 26);

        atomType->RTrim(); resName->RTrim();

        if (!tmpChain->Length()) tmpChain->In(chain->Out());
        
        if (!tmpResN->Length() || strcmp(tmpResN->Out(), resNumber->Out())) {
          tmpResN->In(resNumber->Out());
          resNChange = true;
        }

        if (strcmp(atomType->Out(), resName->Out()) &&
           strcmp("HOH", resName->Out())           &&
           !strcmp(tmpChain->Out(), chain->Out())) {
           String* tmpHETATM = new String(buffer);
           lines->Add(tmpHETATM);
           int* change = new int;
           *change     = (resNChange)?1:0;
           resNChange  = false;
           listResN->Add(change);
        }

        if (atomType)  delete atomType;
        if (resName)   delete resName;
        if (tmpBuffer) delete tmpBuffer;
        if (chain)     delete chain;
        if (resNumber) delete resNumber;

      }
    }

    gzclose(pdbFile);

    if (tmpChain) delete tmpChain;
    if (tmpResN)  delete tmpResN;

    for (int i = listResN->Length() - 1, cumulCount = 0; i >= 0 ; i--, cumulCount++) 
      if (*(listResN->Get(i)) == 1) {
        if (cumulCount < 9)
          for (int j = cumulCount; j >= 0; j--) 
            lines->Delete(i + j);
        cumulCount = 0;
      }

    if (lines->Length())
      this->listIDPDB->Add(listFiles->TransferCurrent());
    else
      listFiles->DeleteCurrent();

  }

  if (lines)      delete lines;
  if (listResN)   delete listResN;
  if (listFiles)  delete listFiles;

  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool ComplexesDB::SplitColumn(char* buffer, TListE<String>* columns, const char* characters) {
 
  if (!buffer || !columns || !characters) return false;

  char *part = strtok(buffer, characters);

  while (part) {
    String* cell = new String(part);
    cell->Trim();
    columns->Add(cell);
    part = strtok(NULL, characters);
  }
  
  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
